package com.kmxd.ams.infra.config;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import cn.hutool.core.text.CharSequenceUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.kmxd.ams.client.system.IConfI18nDetailSvc;
import com.kmxd.ams.client.system.IConfI18nSvc;
import com.kmxd.ams.core.common.bo.LoginUser;
import com.kmxd.ams.core.common.constant.Constants;
import com.kmxd.ams.core.common.constant.ErrorCode;
import com.kmxd.ams.core.exception.*;
import com.kmxd.ams.core.result.R;
import com.kmxd.ams.core.result.ResultCode;
import com.kmxd.ams.core.util.SecurityUtil;
import com.kmxd.ams.infra.system.entity.ConfI18n;
import com.kmxd.ams.infra.system.entity.ConfI18nDetail;
import java.util.Objects;
import java.util.Optional;
import javax.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * 全局异常处理器
 *
 * <p>200 成功提示 500 系统自动 400 业务异常（用于提示） 410 不做提示，直接返回数据
 *
 * @author admin
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
  private static final String CUSTOMIZE_TEMP =
      "==> ams exception:[ ErrorCode:{}, message:{}, debugMessage:{},{} caller:{}, "
          + "method:{} ] \n stack info:";
  private static final String SYS_LOG_TEMP =
      "==> ams exception:[ ErrorCode:{}, message:{}, debugMessage:{} ] \n stack info:";
  @Value("${logging.open-debug-info:false}")
  private boolean openDebug;
  @Autowired private IConfI18nSvc confI18nSvc;
  @Autowired private IConfI18nDetailSvc confI18nDetailSvc;

  /** 业务异常 */
  @ExceptionHandler(CustomException.class)
  public R businessException(CustomException e) {
    log.error(e.getMessage(), e);
    if (Objects.isNull(e.getCode())) {
      return R.fail(e.getMessage());
    }
    return R.result(e.getCode(), e.getMessage());
  }

  @ExceptionHandler(NotLoginException.class)
  public R handleNotLoginException(NotLoginException e) {
    log.error(e.getMessage(), e);
    return R.result(ResultCode.TOKEN_ERROR);
  }

  @ExceptionHandler(NotRoleException.class)
  public R handleNotRoleException(NotRoleException e) {
    log.error(e.getMessage(), e);
    return R.result(ResultCode.UN_AUTHORIZED);
  }

  @ExceptionHandler(NotPermissionException.class)
  public R handleNotPermissionException(NotPermissionException e) {
    log.error(e.getMessage(), e);
    return R.result(ResultCode.UN_AUTHORIZED);
  }

  @ExceptionHandler(RuntimeException.class)
  public R handleRuntimeException(RuntimeException e) {
    log.error(e.getMessage(), e);
    return R.result(ResultCode.INTERNAL_SERVER_ERROR);
  }

  @ExceptionHandler(Exception.class)
  public R handleException(Exception e) {
    log.info(CharSequenceUtil.format(SYS_LOG_TEMP, ErrorCode.EXCEPTION, e.getMessage(), null), e);
    return R.result(ResultCode.INTERNAL_SERVER_ERROR);
  }

  /** 自定义验证异常 */
  @ExceptionHandler(BindException.class)
  public R validatedBindException(BindException e) {
    log.error(e.getMessage(), e);
    String message = e.getAllErrors().get(0).getDefaultMessage();
    return R.fail(message);
  }

  /** 自定义验证异常 */
  @ExceptionHandler(ConstraintViolationException.class)
  public R constraintViolationException(ConstraintViolationException e) {
    log.error(e.getMessage(), e);
    String message = e.getConstraintViolations().iterator().next().getMessage();
    return R.fail(message);
  }

  /** 自定义验证异常 */
  @ExceptionHandler(MethodArgumentNotValidException.class)
  public Object validExceptionHandler(MethodArgumentNotValidException e) {
    log.error(e.getMessage(), e);
    String message = e.getBindingResult().getFieldError().getDefaultMessage();
    return R.fail(message);
  }

  /** 处理自定义异常 */
  @ExceptionHandler(BizException.class)
  public R<Object> handleBizException(BizException e) {
    String errorContent = getI18nErrorContent(ErrorCode.EXCEPTION);
    log.info(
        CharSequenceUtil.format(
            CUSTOMIZE_TEMP,
            e.getCode(),
            errorContent,
            e.getDebugMessage(),
            e.getMsg(),
            e.getClassName(),
            e.getMethodName()),
        e);
    String debugMessage = openDebug ? e.getDebugMessage() : null;
    return R.fail(ResultCode.INTERNAL_SERVER_ERROR, errorContent, e.getCode(), debugMessage);
  }
  /** 提示异常，返回 */
  @ExceptionHandler(BizTipException.class)
  public R<Object> handleBizTipException(BizTipException e) {
    String errorContent = getI18nErrorContent(ErrorCode.TIP_INFO);
    log.info(
        CharSequenceUtil.format(
            CUSTOMIZE_TEMP,
            e.getCode(),
            errorContent,
            e.getDebugMessage(),
            e.getMsg(),
            e.getClassName(),
            e.getMethodName()),
        e);
    String debugMessage = openDebug ? e.getDebugMessage() : null;
    return R.fail(
        ResultCode.FAILURE, errorContent + "【" + e.getCode() + "】", e.getCode(), debugMessage);
  }
  /**
   * validate 参数校验异常
   *
   * @param e 异常信息
   * @return
   */
  @ExceptionHandler(ParamsValidateException.class)
  public R<Object> paramsValidateException(ParamsValidateException e) {
    String errorContent = getI18nErrorContent(e.getCode());
    String fileI18nName = getI18nErrorContent(e.getFieldName());
    log.info(
        CharSequenceUtil.format(
            CUSTOMIZE_TEMP,
            e.getCode(),
            errorContent,
            e.getDebugMessage(),
            e.getMsg(),
            e.getClassName(),
            e.getMethodName()),
        e);
    String debugMessage = openDebug ? e.getDebugMessage() : null;
    return R.fail(
        ResultCode.INTERNAL_SERVER_ERROR, fileI18nName + errorContent, e.getCode(), debugMessage);
  }

  /**
   * 通过错误码，用户语言设置，获取错误提示信息
   *
   * @param code 错误码
   * @return
   */
  private String getI18nErrorContent(String code) {
    LoginUser loginUser = Optional.ofNullable(SecurityUtil.getLoginUser()).orElse(new LoginUser());
    String langCode =
        Optional.ofNullable(loginUser.getLangCode()).orElse(Constants.DEFAULT_LANG_CODE);
    ConfI18n confI18n =
        confI18nSvc.getOne(Wrappers.<ConfI18n>lambdaQuery().eq(ConfI18n::getCode, code));
    if (null == confI18n) {
      return Constants.DEFAULT_LANG_CONTENT;
    }
    ConfI18nDetail confI18nDetailDo =
        confI18nDetailSvc.getOne(
            Wrappers.<ConfI18nDetail>lambdaQuery()
                .eq(ConfI18nDetail::getConfId, confI18n.getId())
                .eq(ConfI18nDetail::getLangCode, langCode));
    if (null == confI18nDetailDo) {
      return Constants.DEFAULT_LANG_CONTENT;
    }
    return confI18nDetailDo.getLangContent();
  }

  @ExceptionHandler({DuplicateKeyException.class})
  public R<Object> handleDuplicateKeyException(DuplicateKeyException e) {
    String errorContent = getI18nErrorContent(ErrorCode.DUPLICATE_KEY);
    log.info(
        CharSequenceUtil.format(
            SYS_LOG_TEMP, ErrorCode.DUPLICATE_KEY, errorContent, e.getMessage()),
        e);

    return R.fail(ResultCode.INTERNAL_SERVER_ERROR, errorContent, ErrorCode.DUPLICATE_KEY, "");
  }

  /**
   * 授权失败异常
   *
   * @param e
   * @return
   */
  @ExceptionHandler({LicenseException.class})
  public R<Object> handleLicenseException(LicenseException e) {
    String errorContent = getI18nErrorContent(ErrorCode.NO_LICENSE);
    log.info(
        CharSequenceUtil.format(SYS_LOG_TEMP, ErrorCode.NO_LICENSE, errorContent, e.getMessage()),
        e);
    return R.fail(ResultCode.NO_LICENSE, errorContent, ErrorCode.NO_LICENSE, "");
  }

  /** 处理自定义异常 */
  @ExceptionHandler(DataCheckException.class)
  public R<Object> handleBizException(DataCheckException e) {
    String errorContent = getI18nErrorContent(e.getCode());
    log.info(
        CharSequenceUtil.format(
            CUSTOMIZE_TEMP,
            e.getCode(),
            errorContent,
            e.getDebugMessage(),
            e.getMsg(),
            e.getClassName(),
            e.getMethodName()),
        e);
    String debugMessage = openDebug ? e.getDebugMessage() : null;
    return R.fail(ResultCode.PARAM_MISS, errorContent, e.getCode(), debugMessage);
  }
}
